package cii_lib;

public class CIIdata {
  private int Width;
  private StringBuffer Buf=null;
  public final int LSIZE =64;
  public final int ISIZE =32;
  public final int BSIZE =8;
  private final int BMASK =(1<<8)-1;
  private final int HEXNUM=2;
  private final int HSIZE=BSIZE/HEXNUM;
  private final int HMASK=(1<<HSIZE)-1;
  
  private void _CIIdata(int width){
    if (width<0)
      try {
	throw new CiiLibException("CCII_DATA/_Init",CiiLibException.MSG.PARAM_ERROR);
      } 
    catch (CiiLibException e) {e.printStackTrace();}
    Width=width;
    int len=(Width-1)/BSIZE+1;
    if (Buf!=null && Buf.length()!=len){
      Buf.setLength(len);
      for(int index=0; index<len;index++)
	Buf.setCharAt(index,(char)0);
    }
    else{
      Buf=new StringBuffer(len);
      for(int index=0; index<len;index++)
	Buf.append((char)0);
    }
  }
  
  public CIIdata(int width){
    _CIIdata(width);
  }

  public CIIdata(CIIdata data){
    _CIIdata(data.Width);
    for(int index=0; index<Buf.length();index++)
	Buf.setCharAt(index,data.Buf.charAt(index));
  }

  public int GetWidth(){
    return(Width);
  }

  public void WidthCut(int cut){
    int width=Width-cut;
    if (cut<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/_IntIns",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    if (cut==0) return;
    int len=(width-1)/BSIZE+1;
    if (Buf.length()>len)
      Buf.setLength(len);
    len=BSIZE*len-width;
    Buf.setCharAt(Buf.length()-1,_CharIns(Buf.charAt(Buf.length()-1),(char)0,BSIZE-len,0,len));
    Width=width;
  }
  
  public void WidthAppend(int append){
    int width=Width+append;
    if (append<0)
      try {
	throw new CiiLibException("CCII_DATA/_IntIns",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    if (append==0) return;
    int len=(width-1)/BSIZE+1;
    for(int index=Buf.length();index<len;index++)
      Buf.append((char)0);
    Width=width;
  }

  public void WidthAdjust(int adjust){
    if (adjust>=0)
      WidthAppend(adjust);
    else
      WidthCut(-adjust);
  }

  public void WidthSet(int width){
    WidthAdjust(width-Width);
  }

  public void Clear(){
    for(int index=0; index<Buf.length();index++)
      Buf.setCharAt(index,(char)0);
  }

  public void Copy(CIIdata data){
    if (data==null)
      try {
	throw new CiiLibException("CCII_DATA/Copy",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    Clear();
    for(int index=0; index<Buf.length() && index<data.Buf.length(); index++)
      Buf.setCharAt(index,data.Buf.charAt(index));
  }

  private char _CharIns(char dst, char src, int dpos, int spos, int len){
    if (dpos<0 || spos<0 ||len<0 || dpos+len>BSIZE || spos+len>BSIZE)
      try {
	throw new CiiLibException("CCII_DATA/_IntIns",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    if(len==0) return(dst);
    int Mask=(1<<len)-1;
    int res = (dst & (~(Mask<<dpos))) | (((src>>spos)&Mask)<<dpos);
    return((char)(res & BMASK));
  }
  
  private void _Insert(int pos, CIIdata val){
    if (pos<0 || pos+val.Width>Width)
      try {
	throw new CiiLibException("CCII_DATA/_Insert",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    if(val.Width==0) return;
    int LIdx=pos/BSIZE;
    int LPos=pos-LIdx*BSIZE;
    int RIdx=(pos+val.Width-1)/BSIZE;
    int RPos=pos+val.Width-1-RIdx*BSIZE;
    if (LIdx==RIdx)
      Buf.setCharAt(LIdx,_CharIns(Buf.charAt(LIdx),val.Buf.charAt(0),LPos,0,val.Width));
    else{
      Buf.setCharAt(LIdx,_CharIns(Buf.charAt(LIdx),val.Buf.charAt(0),LPos,0,BSIZE-LPos));
      for (int index=1; index<RIdx-LIdx; index++){
	if(LPos>0) Buf.setCharAt(LIdx+index,_CharIns(Buf.charAt(LIdx+index),val.Buf.charAt(index-1),0,BSIZE-LPos,LPos));
	Buf.setCharAt(LIdx+index,_CharIns(Buf.charAt(LIdx+index),val.Buf.charAt(index),LPos,0,BSIZE-LPos));
      }
      Buf.setCharAt(RIdx,_CharIns(Buf.charAt(RIdx),val.Buf.charAt(val.Buf.length()-1),0,BSIZE-RPos-1,RPos+1));
    }
  }

  private CIIdata _Extract(int pos, int width){
    if (pos<0 || width<0 || pos+width>Width)
      try {
	throw new CiiLibException("CCII_DATA/_Extract",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    if(width>0){
      int LIdx=pos/BSIZE;
      int LPos=pos-LIdx*BSIZE;
      int RIdx=(pos+width-1)/BSIZE;
      int RPos=pos+width-1-RIdx*BSIZE;
      if (LIdx==RIdx)
	Data.Buf.setCharAt(0,_CharIns(Data.Buf.charAt(0),Buf.charAt(LIdx),0,LPos,width));
      else{
        for (int index=0; index<Data.Buf.length(); index++){
          Data.Buf.setCharAt(index,_CharIns(Data.Buf.charAt(index),Buf.charAt(LIdx+index),0,LPos,BSIZE-LPos));
          if(LPos>0) Data.Buf.setCharAt(index,_CharIns(Data.Buf.charAt(index),Buf.charAt(LIdx+index+1),BSIZE-LPos,0,LPos));
        }
      }
      if((RPos=Data.Buf.length()*BSIZE-width)>0)
	Data.Buf.setCharAt(Data.Buf.length()-1,(char)((Data.Buf.charAt(Data.Buf.length()-1)&((1<<(BSIZE-RPos))-1)) & BMASK));
    }
    return(Data);
  }
  
  public void SetValue(int pos, int width, long data){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetValue",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    for(int index=0; index<Data.Buf.length(); index++){
      Data.Buf.setCharAt(index,(char)(data & BMASK));
      data=data>>BSIZE;
    }
    _Insert(pos,Data);
  }

  public void SetValue(int pos, int width, boolean flag){
    SetValue(pos,width,flag==true?1:0);
  }

  public void SetValue(long data){
    SetValue(0,Width,data);
  }

  public void SetValue(boolean flag){
    SetValue(0,Width,flag==true?1:0);
  }

  public CIIdata(int width, long value){
    _CIIdata(width);
    SetValue(value);
  }
  
  public long GetValue(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/GetValue",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = _Extract(pos, width);
    long res=0;
    for(int index=0; index<Data.Buf.length();index++){
      res=res<<BSIZE;
      res = res | Data.Buf.charAt(Data.Buf.length()-index-1);
    }
    return(res);
  }

  public boolean GetFlag(int pos, int width){
    long res = GetValue(pos,1);
    return(res==0?false:true);
  }

  public long GetValue(){
    return(GetValue(0,Width));
  }

  public boolean GetFlag(){
    return(GetFlag(0,1));
  }

  private int _BinConv(int bin){
    if (bin=='0' || bin=='1') return(bin-'0');
    try {
      throw new CiiLibException("CCII_DATA/BinConv",CiiLibException.MSG.PARAM_ERROR);
    } catch (CiiLibException e) {e.printStackTrace();}
    return(-1);
  }
  
  private String _ZerosCut(String str){
    int cnt=0;
    for (int index=0; index<str.length()-1; index++)
      if (str.charAt(index)=='0')
	cnt++;
      else
	break;
    return(str.substring(cnt));
  }

  public void SetHigh(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetHigh",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    for(int index=0; index<Data.Buf.length(); index++){
      Data.Buf.setCharAt(index,(char)BMASK);
    }
    _Insert(pos,Data);    
  }

  public void SetHigh(){
    SetHigh(0,Width);
  }

  public void SetLow(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetLow",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    _Insert(pos,Data);    
  }

  public void SetLow(){
    SetLow(0,Width);
  }

  public void SetBin(int pos, int width, String bin){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetBin",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    int BinMaxPos=bin.length()-1;
    for(int index=0; index<Data.Buf.length(); index++){
      for (int sh=0, cpos=BSIZE*index; cpos<BSIZE*(index+1) && cpos<=BinMaxPos && cpos<Width; sh++, cpos++)
	Data.Buf.setCharAt(index,(char) ((Data.Buf.charAt(index) | (_BinConv(bin.charAt(BinMaxPos-cpos))<<sh)) & BMASK));
    }
    _Insert(pos,Data);
  }
  
  public void SetBin(String bin){
    SetBin(0,Width,bin);
  }

  public String GetBin0(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/GetBin",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = _Extract(pos, width);
    StringBuffer Res = new StringBuffer(width);
    int BufMaxPos=Data.Buf.length()-1;
    for(int index=0; index<=BufMaxPos; index++){
      int val=Data.Buf.charAt(index);
      for (int cpos=BSIZE*index; cpos<BSIZE*(index+1) && cpos<width; cpos++){
        Res.insert(0,(char)((val&1)==0?'0':'1'));
        val=val>>1;
      }
    }
    return(Res.toString());
  }

  public String GetBin0(){
    return(GetBin0(0,Width));
  }

  public String GetBin(int pos, int width){
    return(_ZerosCut(GetBin0(pos,width)));
  }

  public String GetBin(){
    return(GetBin(0,Width));
  }

  private int _HexConv(int hex){
    if (hex>='0' && hex<='9') return(hex-'0');
    if (hex>='A' && hex<='F') return(hex-'A'+10);
    if (hex>='a' && hex<='f') return(hex-'a'+10);
    try {
      throw new CiiLibException("CCII_DATA/HexConv",CiiLibException.MSG.PARAM_ERROR);
    } catch (CiiLibException e) {e.printStackTrace();}
    return(-1);
  }
  
  public void SetHex(int pos, int width, String hex){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetHex",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    int HexMaxPos=hex.length()-1;
    for(int index=0; index<Data.Buf.length(); index++){
      Data.Buf.setCharAt(index,(char)0);
      for (int sh=0, cpos=HEXNUM*index; cpos<HEXNUM*(index+1) && cpos<=HexMaxPos && cpos*HSIZE<Width; sh++, cpos++)
	Data.Buf.setCharAt(index,(char)((Data.Buf.charAt(index) | (_HexConv(hex.charAt(HexMaxPos-cpos))<< (HSIZE*sh))) & BMASK));
    }
    _Insert(pos,Data);
  }

  public void SetHex(String hex){
    SetHex(0,Width,hex);
  }
  
  public String GetHex0(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/GetHex",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = _Extract(pos, width);
    int Len=(Data.Width-1)/HSIZE+1;
    StringBuffer Res = new StringBuffer(Len);
    int HexMaxPos=Data.Buf.length()-1;
    for(int index=0; index<=HexMaxPos; index++){
      int val=Data.Buf.charAt(index);
      for (int cpos=HEXNUM*index; cpos<HEXNUM*(index+1) && cpos<Len; cpos++){
        int hex=val&HMASK;
        if (hex<=9)
          Res.insert(0,(char)(hex+'0'));
        else
          Res.insert(0,(char)(hex+'A'-10));
        val=val>>HSIZE;
      }
    }
    return(Res.toString());
  }

  public String GetHex0(){
    return(GetHex0(0,Width));
  }

  public String GetHex(int pos, int width){
    return(_ZerosCut(GetHex0(pos,width)));
  }

  public String GetHex(){
    return(GetHex(0,Width));
  }

  private int _DecConv(int dec){
    if (dec>='0' && dec<='9') return(dec-'0');
    try {
      throw new CiiLibException("CCII_DATA/HexConv",CiiLibException.MSG.PARAM_ERROR);
    } catch (CiiLibException e) {e.printStackTrace();}
    return(-1);
  }
  
  public void SetDec(int pos, int width, String str){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetStr",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(str.length()*HSIZE);
    CIIdata Base = new CIIdata(str.length()*HSIZE);
    CIIdata Val = new CIIdata(str.length()*HSIZE);
    Base.SetValue(1);
    Val.Copy(Base);
    for(int index=str.length()-1; index>=0; index--){
      Data.Add(Val.Mult(_DecConv(str.charAt(index))));
      Base.Mult(10);
      Val.Copy(Base);
    }
    Data.WidthSet(Width);
    _Insert(pos,Data);
  }

  public void SetDec(String str){
    SetDec(0,Width,str);
  }

  private StringBuffer _ConvDecChar(int val){
    StringBuffer res = new StringBuffer(0);
    if (val==0)
      res.insert(0,(char)'0');
    else{
      int base=100;
      while (base>0){
	if ((val/base)>0 || res.length()>0)
	  res.insert(0,(char)(('0'+(val/base))&BMASK));
	val=val-base*(val/base);
	base=base/10;
      }
    }
    return(res);
  }

  private StringBuffer _AddDec(StringBuffer arg1, StringBuffer arg2){
    int res=0;
    int Len = Math.max(arg1.length(),arg2.length());
    for (int index=0; index<Len; index++){
      if (arg1.length()==index)
	arg1.append((char)'0');
      res = _DecConv(arg1.charAt(index))+(index<arg2.length()?_DecConv(arg2.charAt(index)):0)+res;
      arg1.setCharAt(index,(char)(('0'+res-10*(res/10))&BMASK));
      res=res/10;
      if (index+1==Len && res>0)
	arg1.append((char)(('0'+res)&BMASK));
    }
    return(arg1);
  }

  private StringBuffer _MultDec(StringBuffer arg1, StringBuffer arg2){
    int Len = arg1.length();
    StringBuffer Data = new StringBuffer(0);
    for (int index=0; index<Len; index++){
      Data.append(arg1.charAt(index));
      arg1.setCharAt(index,(char)'0');
    }
    for (int midx=0; midx<arg2.length(); midx++){
      int mres=0;
      int ares=0;
      for (int index=0; index<Len; index++){
	mres=_DecConv(Data.charAt(index))*_DecConv(arg2.charAt(midx))+mres;
	if (arg1.length()==index+midx)
	  arg1.append((char)'0');
	ares=_DecConv(arg1.charAt(index+midx))+(mres-10*(mres/10))+ares;
	arg1.setCharAt(index+midx, (char)(('0'+ares-10*(ares/10)) & BMASK));
	mres=mres/10;
	ares=ares/10;
	if (index+1==Len && ares+mres>0)
	  arg1.append((char)(('0'+ares+mres)&BMASK));
      }
    }
    return(arg1);
  }

  public String GetDec(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/GetStr",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = _Extract(pos, width);
    StringBuffer Pow  = new StringBuffer(String.valueOf(1<<BSIZE));
    Pow.reverse();
    StringBuffer Base = new StringBuffer("1");
    StringBuffer Res  = _ConvDecChar(Data.Buf.charAt(0));
    for(int index=1; index<Buf.length(); index++){
      _MultDec(Base,Pow);
      StringBuffer data = _ConvDecChar(Data.Buf.charAt(index));
      _MultDec(data, Base);
      _AddDec(Res, data);
    }
    return(_ZerosCut(Res.reverse().toString()));
  }
    
  public String GetDec(){
    return(GetDec(0,Width));
  }

  public void SetStr(int pos, int width, String str){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/SetStr",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(width);
    int StrMaxPos=str.length()-1;
    for(int index=0; index<Data.Buf.length() && index<=StrMaxPos; index++)
      Data.Buf.setCharAt(index,str.charAt(StrMaxPos-index));
    _Insert(pos,Data);
  }

  public void SetStr(String str){
    SetStr(0,Width,str);
  }

  public String GetStr(int pos, int width){
    if (pos<0 || width<0)
      try {
	throw new CiiLibException("CCII_DATA/GetStr",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = _Extract(pos, width);
    int Len=Data.Buf.length();
    StringBuffer Res = new StringBuffer(Len);
    for(int index=0; index<Len; index++)
      Res.append(Data.Buf.charAt(Data.Buf.length()-1-index));
    return(Res.toString());
  }
    
  public String GetStr(){
    return(GetStr(0,Width));
  }

  public void SetUni(int pos, int width, String sval){
    if (sval.length()>2){
      if      (sval.startsWith("0x")) {SetHex(pos,width,sval.substring(2)); return;}
      else if (sval.startsWith("0n")) {SetBin(pos,width,sval.substring(2)); return;}
    }
    SetDec(pos,width,sval);
  }

  public void SetUni(String sval){
    SetUni(0,Width,sval);
  }

  public CIIdata Add(CIIdata arg){
    if (arg==null ||arg.Width==0)
      try {
	throw new CiiLibException("CCII_DATA/Add",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    int res=0;
    for (int index=0; index<Buf.length(); index++){
      res=Buf.charAt(index)+(index<arg.Buf.length()?arg.Buf.charAt(index):0)+res;
      Buf.setCharAt(index, (char)(res & BMASK));
      res>>=BSIZE;
    }
    return(this);
  }

  public CIIdata Add(long arg){
    CIIdata Arg = new CIIdata(LSIZE);
    Arg.SetValue(arg);
    Add(Arg);
    return(this);
  }

  public CIIdata Sub(CIIdata arg){
    if (arg==null ||arg.Width==0)
      try {
	throw new CiiLibException("CCII_DATA/Add",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    for (int index=0; index<arg.Buf.length(); index++){
      arg.Buf.setCharAt(index,(char)(~arg.Buf.charAt(index) & BMASK));
    }
    arg.Add(1);
    int res=0;
    for (int index=0; index<Buf.length(); index++){
      res=Buf.charAt(index)+(index<arg.Buf.length()?arg.Buf.charAt(index):0)+res;
      Buf.setCharAt(index, (char)(res & BMASK));
      res>>=BSIZE;
    }
    return(this);
  }

  public CIIdata Mult(CIIdata arg){
    if (arg==null ||arg.Width==0)
      try {
	throw new CiiLibException("CCII_DATA/Add",CiiLibException.MSG.PARAM_ERROR);
      }
    catch (CiiLibException e) {e.printStackTrace();}
    CIIdata Data = new CIIdata(this);
    Clear();
    for (int midx=0; midx<arg.Buf.length(); midx++){
      int mres=0;
      int ares=0;
      for (int index=0; index+midx<Buf.length(); index++){
	mres=Data.Buf.charAt(index)*arg.Buf.charAt(midx)+mres;
	ares=Buf.charAt(index+midx)+(mres & BMASK)+ares;
	Buf.setCharAt(index+midx, (char)(ares & BMASK));
	mres>>=BSIZE;
	ares>>=BSIZE;
      }
    }
    return(this);
  }

  public CIIdata Mult(long arg){
    CIIdata Arg = new CIIdata(LSIZE);
    Arg.SetValue(arg);
    Mult(Arg);
    return(this);
  }

  public static void WriteVal(CCII_ACCESS_RW id, int num, int grp, long val){
    CIIdata Data = new CIIdata(id.Width());
    Data.SetValue(val);
    id.Write(num,grp,Data);
  }
}
